package com.ruyuan.user.server.domain.service.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.ruyuan.common.model.BusinessException;
import com.ruyuan.common.model.ErrorCode;
import com.ruyuan.common.model.PageDTO;
import com.ruyuan.user.api.model.enums.AuthResourceTypeEnum;
import com.ruyuan.user.api.model.enums.UserJobRelationTypeEnum;
import com.ruyuan.user.server.domain.converter.RoleConverter;
import com.ruyuan.user.server.domain.model.AuthResource;
import com.ruyuan.user.server.domain.model.Authority;
import com.ruyuan.user.server.domain.model.Job;
import com.ruyuan.user.server.domain.model.Role;
import com.ruyuan.user.server.domain.service.AuthorityDomainService;
import com.ruyuan.user.server.domain.service.JobDomainService;
import com.ruyuan.user.server.domain.service.RoleDomainService;
import com.ruyuan.user.server.domain.service.request.*;
import com.ruyuan.user.server.repository.bean.*;
import com.ruyuan.user.server.repository.mapper.JobRoleRelationMapper;
import com.ruyuan.user.server.repository.mapper.RoleAuthorityRelationMapper;
import com.ruyuan.user.server.repository.mapper.RoleMapper;
import com.ruyuan.user.server.repository.mapper.UserRoleRelationMapper;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author xx
 */
@Service
public class RoleDomainServiceImpl implements RoleDomainService {

    @Autowired
    private RoleMapper roleMapper;

    @Autowired
    private RoleConverter roleConverter;

    @Autowired
    private RoleAuthorityRelationMapper roleAuthorityRelationMapper;

    @Autowired
    private UserRoleRelationMapper userRoleRelationMapper;

    @Autowired
    private JobRoleRelationMapper jobRoleRelationMapper;

    @Autowired
    private AuthorityDomainService authorityDomainService;

    @Autowired
    private JobDomainService jobDomainService;

    @Override
    public Long saveRole(Role role) {
        // 防止code重复,一个是建一个唯一索引，另一个是插入前做一个校验
        // 物理删除 删除之后记录就不会存在 code依赖于数据库保持唯一
        // 逻辑删除 删除之后记录还会存在 code依赖业务校验保持唯一
        checkDuplicateCode(role);
        RoleDO roleDO = roleConverter.convertModelToDO(role);
        if (roleDO.getId() == null) {
            roleMapper.insertSelective(roleDO);
        } else {
            roleMapper.updateByPrimaryKeyWithBLOBs(roleDO);
        }
        return roleDO.getId();
    }

    private void checkDuplicateCode(Role role) {
        RoleExample roleExample = new RoleExample();
        roleExample.createCriteria().andOrgIdEqualTo(role.getOrgId())
                .andIsDeletedEqualTo("n").andCodeEqualTo(role.getCode());
        List<RoleDO> roleDOS = roleMapper.selectByExample(roleExample);
        if (role.getId() == null && CollectionUtils.isNotEmpty(roleDOS)) {
            throw new BusinessException(ErrorCode.code("ROLE_CODE_DUPLICATED", role.getCode()));
        }
        if (role.getId() != null && !role.getId().equals(roleDOS.get(0).getId())) {
            throw new BusinessException(ErrorCode.code("ROLE_CODE_DUPLICATED", role.getCode()));
        }
    }

    @Override
    public Role queryById(Long roleId) {
        RoleDO roleDO = roleMapper.selectByPrimaryKey(roleId);
        return roleConverter.convertDOToModel(roleDO);
    }

    @Override
    public PageDTO<Role> queryRoleByPage(PageQueryRoleParam param) {
        RoleExample roleExample = new RoleExample();
        RoleExample.Criteria criteria = roleExample.createCriteria();
        criteria.andIsDeletedEqualTo("n");
        if (StringUtils.isNotBlank(param.getCode())) {
            criteria.andCodeEqualTo(param.getCode());
        }
        if (StringUtils.isNotBlank(param.getName())) {
            criteria.andNameEqualTo(param.getName());
        }
        if (param.getOrgId() != null) {
            criteria.andOrgIdEqualTo(param.getOrgId());
        }
        PageHelper.startPage(param.getCurrent(), param.getPageSize());
        Page<RoleDO> roleDOPage = (Page<RoleDO>) roleMapper.selectByExampleWithBLOBs(roleExample);
        if (roleDOPage == null || CollectionUtils.isEmpty(roleDOPage.getResult())) {
            return PageDTO.emptyPage(param.getPageSize());
        }
        PageDTO<Role> pageResult = new PageDTO<>();
        pageResult.setCurrent(roleDOPage.getPageNum());
        pageResult.setPageSize(roleDOPage.getPageSize());
        pageResult.setCurPageSize(roleDOPage.size());
        pageResult.setTotal(roleDOPage.getTotal());
        pageResult.setPages(roleDOPage.getPages());
        pageResult.setData(roleDOPage.getResult().stream().map(roleConverter::convertDOToModel).collect(Collectors.toList()));
        return pageResult;
    }

    @Override
    public PageDTO<Authority> queryRoleAuthorities(PageQueryRoleAuthoritiesParam param) {
        RoleAuthorityRelationExample roleAuthorityRelationExample = new RoleAuthorityRelationExample();
        RoleAuthorityRelationExample.Criteria criteria = roleAuthorityRelationExample.createCriteria();
        criteria.andRoleIdEqualTo(param.getRoleId());
        if (CollectionUtils.isNotEmpty(param.getTypeEnumList())) {
            criteria.andAuthResourceTypeIn(param.getTypeEnumList().stream().map(AuthResourceTypeEnum::getCode).collect(Collectors.toList()));
        }
        PageHelper.startPage(param.getCurrent(), param.getPageSize(), true);
        Page<RoleAuthorityRelationDO> roleAuthorityDOPage = (Page<RoleAuthorityRelationDO>) roleAuthorityRelationMapper.selectByExample(roleAuthorityRelationExample);
        if (roleAuthorityDOPage == null || CollectionUtils.isEmpty(roleAuthorityDOPage.getResult())) {
            return PageDTO.emptyPage(param.getPageSize());
        }
        PageDTO<Authority> pageResult = new PageDTO<>();
        pageResult.setCurrent(roleAuthorityDOPage.getPageNum());
        pageResult.setPageSize(roleAuthorityDOPage.getPageSize());
        pageResult.setCurPageSize(roleAuthorityDOPage.size());
        pageResult.setTotal(roleAuthorityDOPage.getTotal());
        pageResult.setPages(roleAuthorityDOPage.getPages());
        List<Long> authorityIds = roleAuthorityDOPage.getResult().stream().map(RoleAuthorityRelationDO::getAuthorityId).collect(Collectors.toList());
        pageResult.setData(authorityDomainService.queryAuthorityByIds(authorityIds));
        return pageResult;
    }

    @Override
    public void removeRoleAuthorities(ChangeRoleAuthorityParam param) {
        if (CollectionUtils.isEmpty(param.getAuthorityList())) {
            return;
        }
        RoleAuthorityRelationExample roleAuthorityRelationExample = new RoleAuthorityRelationExample();
        roleAuthorityRelationExample.createCriteria()
                .andRoleIdEqualTo(param.getRoleId())
                .andAuthorityIdIn(param.getAuthorityList().stream().map(Authority::getId).collect(Collectors.toList()));
        roleAuthorityRelationMapper.deleteByExample(roleAuthorityRelationExample);
    }

    @Override
    public void addRoleAuthorities(ChangeRoleAuthorityParam param) {
        if (CollectionUtils.isEmpty(param.getAuthorityList())) {
            return;
        }
        List<Long> resourceIds = param.getAuthorityList().stream().map(Authority::getAuthResourceId).distinct().collect(Collectors.toList());
        List<AuthResource> authResources = authorityDomainService.queryResourcesByIds(resourceIds);
        Map<Long, AuthResource> resourceIdMap = authResources.stream().collect(Collectors.toMap(AuthResource::getId, p -> p));
        param.getAuthorityList().stream().map(authority -> {
            RoleAuthorityRelationDO roleAuthorityRelationDO = new RoleAuthorityRelationDO();
            roleAuthorityRelationDO.setRoleId(param.getRoleId());
            roleAuthorityRelationDO.setAuthorityId(authority.getId());
            roleAuthorityRelationDO.setAuthResourceId(authority.getAuthResourceId());
            roleAuthorityRelationDO.setAuthResourceType(resourceIdMap.get(authority.getAuthResourceId()).getType().getCode());
            roleAuthorityRelationDO.setCreator(param.getOperator());
            roleAuthorityRelationDO.setModifier(param.getOperator());
            roleAuthorityRelationDO.setIsDeleted("n");
            roleAuthorityRelationDO.setGmtCreate(new Date());
            roleAuthorityRelationDO.setGmtModified(new Date());
            return roleAuthorityRelationDO;
        }).forEach(roleAuthorityRelationMapper::insertSelective);
    }

    @Override
    public List<Role> queryUserRole(Long userId) {
        UserRoleRelationExample userRoleRelationExample = new UserRoleRelationExample();
        userRoleRelationExample.createCriteria().andUserIdEqualTo(userId);
        List<UserRoleRelationDO> userRoleRelationDOList = userRoleRelationMapper.selectByExample(userRoleRelationExample);
        if (CollectionUtils.isEmpty(userRoleRelationDOList)) {
            return new ArrayList<>();
        }
        List<Long> roleIds = userRoleRelationDOList.stream().map(UserRoleRelationDO::getRoleId).collect(Collectors.toList());
        RoleExample roleExample = new RoleExample();
        roleExample.createCriteria().andIdIn(roleIds);
        List<RoleDO> roleDOList = roleMapper.selectByExampleWithBLOBs(roleExample);
        if (CollectionUtils.isEmpty(roleDOList)) {
            return new ArrayList<>();
        }
        return roleDOList.stream().map(roleConverter::convertDOToModel).collect(Collectors.toList());
    }

    @Override
    public void addUserRole(ChangeUserRoleParam param) {
        if (CollectionUtils.isEmpty(param.getRoleIdList())) {
            return;
        }
        param.getRoleIdList().stream().map(roleId -> {
            UserRoleRelationDO userRoleRelationDO = new UserRoleRelationDO();
            userRoleRelationDO.setUserId(param.getUserId());
            userRoleRelationDO.setRoleId(roleId);
            userRoleRelationDO.setCreator(param.getOperator());
            userRoleRelationDO.setModifier(param.getOperator());
            userRoleRelationDO.setIsDeleted("n");
            userRoleRelationDO.setGmtCreate(new Date());
            userRoleRelationDO.setGmtModified(new Date());
            return userRoleRelationDO;
        }).forEach(userRoleRelationMapper::insertSelective);
    }

    @Override
    public void removeUserRole(ChangeUserRoleParam param) {
        if (CollectionUtils.isEmpty(param.getRoleIdList())) {
            return;
        }
        UserRoleRelationExample userRoleRelationExample = new UserRoleRelationExample();
        userRoleRelationExample.createCriteria()
                .andUserIdEqualTo(param.getUserId())
                .andRoleIdIn(param.getRoleIdList());
        userRoleRelationMapper.deleteByExample(userRoleRelationExample);
    }

    @Override
    public List<Role> queryJobRole(Long jobId) {
        JobRoleRelationExample jobRoleRelationExample = new JobRoleRelationExample();
        jobRoleRelationExample.createCriteria().andJobIdEqualTo(jobId);
        List<JobRoleRelationDO> jobRoleRelationDOList = jobRoleRelationMapper.selectByExample(jobRoleRelationExample);
        if (CollectionUtils.isEmpty(jobRoleRelationDOList)) {
            return new ArrayList<>();
        }
        List<Long> roleIds = jobRoleRelationDOList.stream().map(JobRoleRelationDO::getRoleId).collect(Collectors.toList());
        RoleExample roleExample = new RoleExample();
        roleExample.createCriteria().andIdIn(roleIds);
        List<RoleDO> roleDOList = roleMapper.selectByExampleWithBLOBs(roleExample);
        if (CollectionUtils.isEmpty(roleDOList)) {
            return new ArrayList<>();
        }
        return roleDOList.stream().map(roleConverter::convertDOToModel).collect(Collectors.toList());
    }

    @Override
    public void addJobRole(ChangeJobRoleParam param) {
        if (CollectionUtils.isEmpty(param.getRoleIdList())) {
            return;
        }
        param.getRoleIdList().stream().map(roleId -> {
            JobRoleRelationDO jobRoleRelationDO = new JobRoleRelationDO();
            jobRoleRelationDO.setJobId(param.getJobId());
            jobRoleRelationDO.setRoleId(roleId);
            jobRoleRelationDO.setCreator(param.getOperator());
            jobRoleRelationDO.setModifier(param.getOperator());
            jobRoleRelationDO.setIsDeleted("n");
            jobRoleRelationDO.setGmtCreate(new Date());
            jobRoleRelationDO.setGmtModified(new Date());
            return jobRoleRelationDO;
        }).forEach(jobRoleRelationMapper::insertSelective);
    }

    @Override
    public void removeJobRole(ChangeJobRoleParam param) {
        if (CollectionUtils.isEmpty(param.getRoleIdList())) {
            return;
        }
        JobRoleRelationExample jobRoleRelationExample = new JobRoleRelationExample();
        jobRoleRelationExample.createCriteria()
                .andJobIdEqualTo(param.getJobId())
                .andRoleIdIn(param.getRoleIdList());
        jobRoleRelationMapper.deleteByExample(jobRoleRelationExample);
    }

    @Override
    public List<Authority> queryUserAllAuthorities(QueryUserAuthoritiesParam param) {
        // 查出当前用户拥有的角色
        List<Long> allRoleIds = new ArrayList<>();
        UserRoleRelationExample userRoleRelationExample = new UserRoleRelationExample();
        userRoleRelationExample.createCriteria().andUserIdEqualTo(param.getUserId());
        List<UserRoleRelationDO> userRoleRelationDOS = userRoleRelationMapper.selectByExample(userRoleRelationExample);
        List<Long> roleIdByUser = userRoleRelationDOS.stream().map(UserRoleRelationDO::getRoleId).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(roleIdByUser)) {
            allRoleIds.addAll(roleIdByUser);
        }
        // 根据用户拥有的岗位查出间接拥有的角色
        Map<UserJobRelationTypeEnum, List<Job>> userJobMap = jobDomainService.queryUserJobs(param.getUserId());
        List<Long> userAllJobIds = userJobMap.values().stream().flatMap(Collection::stream).map(Job::getId).collect(Collectors.toList());
        JobRoleRelationExample jobRoleRelationExample = new JobRoleRelationExample();
        jobRoleRelationExample.createCriteria().andJobIdIn(userAllJobIds);
        List<JobRoleRelationDO> jobRoleRelationDOS = jobRoleRelationMapper.selectByExample(jobRoleRelationExample);
        List<Long> roleIdByJob = jobRoleRelationDOS.stream().map(JobRoleRelationDO::getRoleId).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(roleIdByJob)) {
            allRoleIds.addAll(roleIdByJob);
        }
        if (allRoleIds.isEmpty()) {
            return new ArrayList<>();
        }
        // 根据角色列表查出角色拥有的权限集
        RoleAuthorityRelationExample roleAuthorityRelationExample = new RoleAuthorityRelationExample();
        RoleAuthorityRelationExample.Criteria criteria = roleAuthorityRelationExample.createCriteria();
        criteria.andRoleIdIn(allRoleIds);
        if (CollectionUtils.isNotEmpty(param.getTypeEnumList())) {
            criteria.andAuthResourceTypeIn(param.getTypeEnumList().stream().map(AuthResourceTypeEnum::getCode).collect(Collectors.toList()));
        }
        if (param.getAuthResourceId() != null) {
            criteria.andAuthResourceIdEqualTo(param.getAuthResourceId());
        }
        List<RoleAuthorityRelationDO> roleAuthorityRelationDOS = roleAuthorityRelationMapper.selectByExample(roleAuthorityRelationExample);
         if(CollectionUtils.isEmpty(roleAuthorityRelationDOS)){
            return new ArrayList<>();
        }
        List<Long> allAuthorityIds = roleAuthorityRelationDOS.stream().map(RoleAuthorityRelationDO::getAuthorityId).collect(Collectors.toList());
        return authorityDomainService.queryAuthorityByIds(allAuthorityIds);
    }
}
